#include "TypeType.h"

#include "PlaceholderType.h"
#include "TypeOf.h"
#include "FunctionList.h"

#include "NativeFunction.h"

#include "TypeInstance.h"

CString CTypeOfType::PlaceholderName = U"T";

CTypeOfType::CTypeOfType()
    :CType(
        STypeID(U"TTypeOf",true)
        ,ETypeFlags::Generic | ETypeFlags::ScriptType
        )
{
    GenericInfo=new CGenericInfo();

    CType *PlaceholderType= new CPlaceholderType(
        STypeID(GetFullName()+U"::"+PlaceholderName,true)
        ,ETypeFlags::None
        ,0
        ,this);
    GenericInfo->PlaceholderTypes.Add(PlaceholderType);

    Functions=new CFunctionList(this);
    {
        //add ToString virtual function
        CNativeFunction *NativeFunction = new CNativeFunction(
            Functions
            ,U"ToString"
            , CGetType<CString>()()
            , {}
            ,{}
            );

        Functions->VirtualFunctionTable.Add(NativeFunction->ID);

    }
}

static CTypeOfType * CreateTypeOfType()
{
    return new CTypeOfType();
}

uint8_t RegisterTypeOfTypeCaller=(CTypeOfType::GetStaticType(),0);

CTypeOfType *CTypeOfType::GetStaticType()
{
    static CTypeOfType *StaticType=CreateTypeOfType();
    return StaticType;
}

CString CTypeOfType::CreateTypeName(const CString &ElementType)
{
    return STypeID::CreateTemplateTypeName(
        CTypeOfType::GetStaticType()->GetFullName()
        ,{ElementType}
        ,{PlaceholderName}
        );
}

STypeID CTypeOfType::CreateID(const SDecoratedType &ElementType)
{
    return STypeID(
        CTypeOfType::GetStaticType()->ID
        ,{ElementType.BasicType->ID}
        ,{PlaceholderName}
        );
}

class CTypeOfInstance : public ITypeInstance
{
protected:
    CTypeOfGeneralized * Type;
public:
    CTypeOfInstance(CTypeOfGeneralized * InType)
        :Type(InType)
    {
    }

    void InitInstance(void *Memory) override
    {
        new (Memory) STypeOfBase(Type->GetElementType());
    }

    void CloneInstance(const void *Instance, void *Memory) override
    {
        new (Memory) STypeOfBase(Type->GetElementType());
    }

    void EraseInstance(void *InValue) override
    {
        // do nothing
    }

    void *CreateInstance() override
    {
        return new STypeOfBase(Type->GetElementType());
    }

    void ResetAllocated(void *InMemory) override
    {
        reinterpret_cast<STypeOfBase *>(InMemory)->Type = Type->GetElementType();
    }

    uint32_t GetSize() override
    {
        return sizeof(STypeOfBase);
    }

    uint64_t GetInstanceHash(const void *InInstance) const override
    {
        return (uint64_t) Type->GetElementType();
    }

    bool IsInstanceEqual(const void *A, const void *B) const override
    {
        return reinterpret_cast<const STypeOfBase *>(A)->Type
            == reinterpret_cast<const STypeOfBase *>(B)->Type;
    }

    CString ToDebugString(const void *InInstance) const override
    {
        return U"TypeOf("+ reinterpret_cast<const STypeOfBase *>(InInstance)->Type->GetFullName()+U")";
    }
};

CTypeOfGeneralized::CTypeOfGeneralized(CType *InElementType)
    :CType(
        CTypeOfType::CreateID(InElementType)
        , ETypeFlags::Generic | ETypeFlags::ScriptType
       )
{
    //typeOf<T> can only be used in script
    //so the element type must be avaliable in script
    assert(InElementType->CanUseInScript());

    Instancable=new CTypeOfInstance(this);

    GeneralizedInfo=new CGeneralizedInfo();
    GeneralizedInfo->TemplateType=CTypeOfType::GetStaticType();
    GeneralizedInfo->TypeArguments.Add(InElementType);

    GeneralizedInfo->TemplateType->GenericInfo->GeneralizedTypes.Add(this);
    reinterpret_cast<CGenericInfo *>(GeneralizedInfo->TemplateType->GenericInfo)
        ->GenericMap.Add(TVector<CType*>({InElementType}), this);

    Functions=new CFunctionList(this);
    {
        //add ToString virtual function
        CNativeFunction *NativeFunction = new CNativeFunction(
            Functions
            ,U"ToString"
            , CGetType<CString>()()
            , {}
            ,{}
            );

        NativeFunction->SetCppBody([this](const TVector<CAny> &InArgs)->CAny
        {
            STypeOfBase * TypeOfBase= *reinterpret_cast<STypeOfBase **>(InArgs[0].GetPointer());
            return TypeOfBase->Type->GetFullName();
        });

        Functions->VirtualFunctionTable.Add(NativeFunction->ID);

    }

}

CType *CTypeOfGeneralized::GetElementType()
{
    return GeneralizedInfo->TypeArguments[0];
}

CString CTypeOfGeneralized::CreateTypeName(CType *ElementType)
{
    return CTypeOfType::GetStaticType()->GetFullName()+ U"<"+ ElementType->GetFullName() +U">";
}
